<template>
  <Moveable
    v-if="target"
    ref="moveableRef"
    :ables="ables"
    :props="{ editable }"
    :target="target"
    :resizable="resizable"
    :draggable="draggable"
    :bounds="{ left: 1, top: 1, right: 1, bottom: 1, position: 'css' }"
    :rotatable="rotatable"
    :scalable="scalable"
    :keepRatio="keepRatio"
    :throttleScale="1"
    :throttleResize="1"
    :throttleDrag="1"
    :snappable="true"
    :rootContainer="rootContainer"
    :verticalGuidelines="verticalGuidelines"
    :horizontalGuidelines="horizontalGuidelines"
    :snapDirections="{
      top: true,
      left: true,
      bottom: true,
      right: true,
      center: true,
      middle: true
    }"
    :elementSnapDirections="{
      top: true,
      left: true,
      bottom: true,
      right: true,
      center: true,
      middle: true
    }"
    :elementGuidelines="elementGuidelines"
    @drag="handleOnDrag"
    @resize="handleOnResize"
    @rotate="handleOnRotate"
    @scale="handleScale"
  ></Moveable>
</template>

<script setup lang="ts">
import Moveable from 'vue3-moveable'
import { nextTick, ref, watch } from 'vue'
import { Editable } from './edit'
import { debounce, isFunction } from 'lodash-es'
import type { IBaseControlProps } from '@/components/BaseControl/types'
import { setPxValue } from '@/utils'

const props = withDefaults(defineProps<IBaseControlProps>(), {
  active: true,
  resizable: true,
  draggable: true,
  rootContainer: document.body
})

const moveableRef = ref()
const emit = defineEmits(['onSetStyle', 'onResize', 'onScale', 'onDrag', 'onRotate'])

const getMoveable = () => {
  return moveableRef.value
}

//更新
const handleUpdate = () => {
  nextTick(() => {
    moveableRef.value.updateRect()
    moveableRef.value.updateSelectors()
    moveableRef.value.updateTarget()
  })
}

const debounceUpdate = debounce(handleUpdate, 200)

//目标元素
const targetRef = ref<any>()
const handleGetTarget = () => {
  if (typeof props.target === 'string') {
    targetRef.value = document.querySelector(props.target)
  } else {
    targetRef.value = props.target
  }
}

//处理旋转
const handleOnRotate = (e: any) => {
  const params = {
    e,
    targetRef,
    moveableRef
  }
  if (isFunction(props.onRotate)) {
    props.onRotate!(params)
    return
  }
  e.target.style.transform = e.drag.transform
  emit('onRotate', params)
  emit('onSetStyle', {
    transform: e.drag.transform
  })
}

//处理缩放
const handleScale = (e: any) => {
  const params = {
    e,
    targetRef,
    moveableRef
  }
  if (isFunction(props.onScale)) {
    props.onScale!(params)
    return
  }
  e.target.style.transform = e.drag.transform
  emit('onScale', params)
  emit('onSetStyle', {
    transform: e.drag.transform
  })
}

//处理拖拽
const handleOnDrag = (e: any) => {
  const params = {
    e,
    targetRef,
    moveableRef
  }
  if (isFunction(props.onDrag)) {
    props.onDrag!(params)
    handleUpdate()
    return
  }
  const left = setPxValue(e.left)
  const top = setPxValue(e.top)
  e.target.style.left = left
  e.target.style.top = top
  handleUpdate()
  emit('onDrag', params)
  emit('onSetStyle', {
    left,
    top
  })
}
//处理缩放
const debounceResizeHeight = debounce(() => {
  targetRef.value.style.height = `fit-content`
  moveableRef.value.updateRect()
}, 200)
const handleOnResize = async (e: any) => {
  const params = {
    e,
    targetRef,
    moveableRef
  }
  if (isFunction(props.onResize)) {
    props.onResize!(params)
    return
  }
  const width = setPxValue(e.width)
  const height = setPxValue(e.height)
  const transform = setPxValue(e.transform)
  e.target.style.width = width
  e.target.style.height = height
  e.target.style.transform = transform
  if (props.resizeHeightFix) {
    debounceResizeHeight()
  }
  emit('onResize', params)
  emit('onSetStyle', {
    width,
    height: props.resizeHeightFix ? 'fit-content' : height,
    transform
  })
}
//扩展能力
const ables = ref<any[]>([])
const handleAbles = () => {
  const { editable, onEditSize } = props
  if (editable && isFunction(onEditSize)) {
    const able = Editable((type: string) =>
      onEditSize!({
        type,
        targetRef,
        moveableRef
      })
    )
    ables.value.push(able)
  }
}

//初始化
const init = () => {
  if (!props.target) {
    return
  }
  nextTick(() => {
    handleGetTarget()
    handleAbles()
    handleUpdate()
  })
}

watch(() => props.target, init, {
  immediate: true,
  deep: true
})

defineExpose({
  getMoveable,
  update: handleUpdate,
  debounceUpdate
})
</script>

<style scoped>
:deep(.moveable-origin) {
  display: none;
}

:deep(.moveable-control) {
  width: 8px !important;
  height: 8px !important;
  border-width: 1px !important;
  border-radius: 0 !important;
}

:deep(.moveable-ne) {
  margin-top: -8px !important;
  margin-left: 0 !important;
}
:deep(.moveable-n) {
  margin-top: -9px !important;
  margin-left: -4px !important;
}

:deep(.moveable-e) {
  margin-top: -4px !important;
  margin-left: 0 !important;
}

:deep(.moveable-w) {
  margin-top: -4px !important;
  margin-left: -8px !important;
}
:deep(.moveable-sw) {
  margin-top: 0 !important;
  margin-left: -8px !important;
}

:deep(.moveable-s) {
  margin-top: 1px !important;
  margin-left: -4px !important;
}
:deep(.moveable-se) {
  margin-top: 0 !important;
  margin-left: 0 !important;
}

:deep(.moveable-rotation-control) {
  transform: translateX(5px) !important;
}
</style>
